/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import rules.ColumnOneFree;
import rules.ColumnSeveralFree;
import rules.*;
import types.ISudokuMatrix;

/**
 *
 * @author kostja
 */
public class RuleHelper {

    static public int[] getDigittsSameRow(int row, ISudokuMatrix board) throws Exception {

        if (row < 0 || row > 8) {
            throw new Exception("Invalid row number");
        }

        int a[] = new int[9];
        for (int i = 0; i < 9; i++) {
            a[i] = (Integer) board.getElementAt(row, i);
        }
        return a;
    }

    static public int[] getDigistsSameCol(int col, ISudokuMatrix board) throws Exception {

        if (col < 0 || col > 8) {
            throw new Exception("Invalid row number");
        }

        int[] a = new int[9];
        for (int i = 0; i < 9; i++) {
            a[i] = (Integer) board.getElementAt(i, col);
        }
        return a;
    }
    static public boolean existsDigitInCol (int digit,int col,ISudokuMatrix board){
        // prüft ob Zahl digit in der Spalte col vorhanden ist
        try {
            int[] colDigits;
            colDigits = getDigistsSameCol(col, board);
            for (int i = 0; i<colDigits.length; i++){
             if(colDigits[i] == digit)
                 return true;
            }
        } catch (Exception ex) {
            Logger.getLogger(RuleHelper.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }
        static public boolean existsDigitInRow (int digit,int row,ISudokuMatrix board){
        // prüft ob Zahl digit in der Zeile row vorhanden ist
        try {
            int[] rowDigits;
            rowDigits = getDigittsSameRow(row, board);
            for (int i = 0; i<rowDigits.length; i++){
             if(rowDigits[i] == digit)
                 return true;
            }
        } catch (Exception ex) {
            Logger.getLogger(RuleHelper.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }


    static public Collection getDigitsSameQuadrat(int row, int col, ISudokuMatrix board) throws Exception {

        if (row < 0 || row > 8 || col < 0 || col > 8) {
            throw new Exception("Invalid row|col number");
        }

        int quadrat = calculateQuadratNumber(row, col);

        Collection digits = new ArrayList();

        if (quadrat == 1) {
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    digits.add(board.getElementAt(i, j));
                }
            }
            return digits;
        } else if (quadrat == 2) {
            for (int i = 0; i < 3; i++) {
                for (int j = 3; j < 6; j++) {
                    digits.add(board.getElementAt(i, j));
                }
            }
            return digits;
        } else if (quadrat == 3) {
            for (int i = 0; i < 3; i++) {
                for (int j = 6; j < 9; j++) {
                    digits.add(board.getElementAt(i, j));
                }
            }
            return digits;
        } else if (quadrat == 4) {
            for (int i = 3; i < 6; i++) {
                for (int j = 0; j < 3; j++) {
                    digits.add(board.getElementAt(i, j));
                }
            }
            return digits;
        } else if (quadrat == 5) {
            for (int i = 3; i < 6; i++) {
                for (int j = 3; j < 6; j++) {
                    digits.add(board.getElementAt(i, j));
                }
            }
            return digits;
        } else if (quadrat == 6) {
            for (int i = 3; i < 6; i++) {
                for (int j = 6; j < 9; j++) {
                    digits.add(board.getElementAt(i, j));
                }
            }
            return digits;
        } else if (quadrat == 7) {
            for (int i = 6; i < 9; i++) {
                for (int j = 0; j < 3; j++) {
                    digits.add(board.getElementAt(i, j));
                }
            }
            return digits;
        } else if (quadrat == 8) {
            for (int i = 6; i < 9; i++) {
                for (int j = 3; j < 6; j++) {
                    digits.add(board.getElementAt(i, j));
                }
            }
            return digits;
        } else if (quadrat == 9) {
            for (int i = 6; i < 9; i++) {
                for (int j = 6; j < 9; j++) {
                    digits.add(board.getElementAt(i, j));
                }
            }
            return digits;
        }
        throw new Exception("invalid field!");
    }

    /**
     * überprüft ob die anzahl der leere Felder in einer Spalte innerhalg des Quadrats
     * der erlaubte Anzahl (numberOfAllowedZeros) überschreitet.
     * wenn ja liefert false
     * wenn nicht true
     *
     * @param row
     * @param col
     * @param board
     * @param numberOfAllowedZeros
     * @return
     * @throws Exception
     */
    static public boolean zerosInTheSameColSameQuadratByAllowedNumber(int row, int col, ISudokuMatrix board, int numberOfAllowedZeros) throws Exception {
        int quadrat = calculateQuadratNumber(row, col);
        System.out.println("\tQuadrat: "+quadrat);
        int[] coldigits = getDigistsSameCol(col, board);
        int num = 0;
        if (quadrat > 0 && quadrat < 4) {

            for (int i = 0; i < 3; i++) {
                if (coldigits[i] == 0) {
                    num++;
                }
            }
        } else if (quadrat > 3 && quadrat < 7) {

            for (int i = 3; i < 6; i++) {
                if (coldigits[i] == 0) {
                    num++;
                }
            }
        } else {
            for (int i = 6; i < 9; i++) {
                if (coldigits[i] == 0) {
                    num++;
                }
            }
        }
        System.out.println("\tNumber of zeros in col "+col+" in quadrat must be 1");
        System.out.println("\tNumber of zeros in col "+col+" in quadrat is " + num);
        if (num > numberOfAllowedZeros) {
            return false;
        } else {
            return true;
        }
    }

    static public Collection getMissingdigitsInQuadratOrColOrRow(Collection quadrat) {
        Collection a = new ArrayList();


        for (int i = 1; i < 10; i++) {
            Iterator iter = quadrat.iterator();
            boolean founded = false;
            while (iter.hasNext()) {
                Integer b = (Integer) iter.next();
                if (b == i) {
                    founded = true;
                    break;
                }
            }
            if (!founded) {
                a.add(i);
            }
        }
        return a;
    }

    /**
     * caqlculates the quadrat a field belongs to
     * @param row
     * @param col
     * @return
     * * sudokuboard:
     *  +++++++++++++
     *  + 1 + 2 + 3 +
     *  +++++++++++++
     *  + 4 + 5 + 6 +
     *  +++++++++++++
     *  + 7 + 8 + 9 +
     *  +++++++++++++
     */
    static public int calculateQuadratNumber(int row, int col) throws Exception {
        if (row < 3) {
            if (col < 3) {
                return 1;
            } else if (col < 6) {
                return 2;
            } else if (col < 9) {
                return 3;
            }
        } else if (row < 6) {
            if (col < 3) {
                return 4;
            } else if (col < 6) {
                return 5;
            } else if (col < 9) {
                return 6;
            }
        } else if (row < 9) {
            if (col < 3) {
                return 7;
            } else if (col < 6) {
                return 8;
            } else if (col < 9) {
                return 9;
            }
        }

        throw new Exception("invalid field!");
    }

    public static boolean arrayIncludesdigit(int[] a, int digit) {
        for (int i = 0; i < 9; i++) {
            if (a[i] == digit) {
                return true;
            }
        }
        return false;
    }

    public static boolean zerosInTheSameRowSameQuadratByAllowedNumber(int row, int col, ISudokuMatrix matrix, int numberOfAllowedZeros) throws Exception {
        int quadrat = calculateQuadratNumber(row, col);

        int[] rowdigits = getDigittsSameRow(row, matrix);
        int num = 0;

        System.out.println("\tQuadrat: "+quadrat);

        int upperLimit = getRowUpperLimit(quadrat);
        
        for (int i = upperLimit-2; i <= upperLimit; i++) {
            if (rowdigits[i] == 0) {
                num++;
            }
        }

        System.out.println("\tNumber of zeros in row "+row+" in quadrat must be 1");
        System.out.println("\tNumber of zeros in row "+row+" in quadrat is " + num);
        
        if (num > numberOfAllowedZeros) {
            return false;
        } else {
            return true;
        }

    }

    static public int getCulUpperLimit(int quadratNumber) throws Exception {
        if (0 < quadratNumber && quadratNumber < 4) {
            return 2;
        } else if (quadratNumber < 7) {
            return 5;
        } else if (quadratNumber < 10) {
            return 8;
        }
        throw new Exception("illegal Quadratnumber");
    }

    static public int getRowUpperLimit(int quadratNumber) throws Exception {
        if (1 > quadratNumber || quadratNumber > 9) {
            throw new Exception("illegal Quadratnumber");
        }

        if (quadratNumber == 1 || quadratNumber == 4 || quadratNumber == 7) {
            return 2;
        } else if (quadratNumber == 2 || quadratNumber == 5 || quadratNumber == 8) {
            return 5;
        } else {
            return 8;
        }
    }
     static public boolean checkOtherFieldsInBlockOfClosedForDigit(int digit,int row,int col, ISudokuMatrix board,int countMissedDigits){
        //prüft alle anderen leeren Feldern im Quadrat, ob diese für die Zahl digit gesperrt sind, wenn alle gesperrt, liefert true zurück
         boolean closed=false;
         try {
            int qnr = calculateQuadratNumber(row, col);
            int maxCol1=getRowUpperLimit(qnr);
            int maxColtemp=maxCol1;
            int maxRow1=getCulUpperLimit(qnr);
            int maxRowtemp=maxRow1;
            int countClosedDigits=0;
            for(int i=0; i<3 ;){
                for(int j=0; j <3;){
                    if ((Integer)(board.getElementAt(maxRowtemp, maxColtemp)) == 0 && !((row == maxRowtemp) && (col == maxColtemp))){
                    if ((RuleHelper.existsDigitInCol(digit,maxColtemp,board) || RuleHelper.existsDigitInRow(digit,maxRowtemp,board)))
                        countClosedDigits++;
                        if (countClosedDigits == countMissedDigits-1)// minus 1 weil ausgewählte Feld soll ja nicht gesperrt werden
                        closed=true;
                    }
                     j++;
                     maxRowtemp=maxRowtemp-1;
                    }
                 i++;
                 maxRowtemp=maxRow1;
                 maxColtemp=maxColtemp-1;
                }
            
            
        } catch (Exception ex) {
            Logger.getLogger(RuleHelper.class.getName()).log(Level.SEVERE, null, ex);
        }
         return closed;
     }

    public static Collection intArrayToCollection(int[] digits) {
        Collection digitsCol = new ArrayList();
            for (int i =0;i<digits.length;i++)
            {
                digitsCol.add(digits[i]);
            }
        return digitsCol;
    }

    public static boolean existsDigitInQuadrat(int digit, int row, int col, ISudokuMatrix matrix) throws Exception {

       Collection digits = RuleHelper.getDigitsSameQuadrat( row, col, matrix);

       Iterator iter = digits.iterator();
       while (iter.hasNext())
       {
        Integer a = (Integer) iter.next();
        if (a == digit)
            return true;
       }
       return false;
    }

    public static void printCollection(Collection a)
    {
        Iterator iter = a.iterator();
        while (iter.hasNext())
        {
            System.out.print(iter.next());
        }
    }

    //liefert eine geklonte collection zurück (keine referenz)
    public static Collection cloneCollection (Collection col)
    {
        Collection clonedCol = new ArrayList();
        Iterator iter = col.iterator();
        while (iter.hasNext())
        {
            clonedCol.add(iter.next());
        }

        return clonedCol;
    }

    public static int[] collectionToIntArray(Collection digitsSameQuadrat) {
        Iterator iter = digitsSameQuadrat.iterator();
        int[] a = new int[9] ;
        int i = 0;
        while (iter.hasNext())
        {
            Integer b = (Integer) iter.next();
            if (b != 0)
                a[i]=b.intValue();
            i++;
        }
        return a;
    }

    public static int calculateRowUsingQuadrat(int quadratNumber, int positionInQuadrat)
    {
        if (positionInQuadrat >=0 && positionInQuadrat <3)
        {
            if (quadratNumber == 1 || quadratNumber == 2 || quadratNumber == 3)
            {
                return 0;
            }
            else if (quadratNumber == 4 || quadratNumber == 5 || quadratNumber == 6)
            {
                return 3;
            }
            else
                return 6;
        }
        else if (positionInQuadrat >2 && positionInQuadrat <6)
        {
            if (quadratNumber == 1 || quadratNumber == 2 || quadratNumber == 3)
            {
                return 1;
            }
            else if (quadratNumber == 4 || quadratNumber == 5 || quadratNumber == 6)
            {
                return 4;
            }
            else
                return 7;

        }
        else
        {
            if (quadratNumber == 1 || quadratNumber == 2 || quadratNumber == 3)
            {
                return 2;
            }
            else if (quadratNumber == 4 || quadratNumber == 5 || quadratNumber == 6)
            {
                return 5;
            }
            else
                return 8;
        }
    }
    public static int calculateColUsingQuadrat(int quadratNumber, int positionInQuadrat)
    {
         if (positionInQuadrat == 0 || positionInQuadrat == 3 || positionInQuadrat == 6)
        {
            if (quadratNumber == 1 || quadratNumber == 4 || quadratNumber == 7)
            {
                return 0;
            }
            else if (quadratNumber == 2 || quadratNumber == 5 || quadratNumber == 8)
            {
                return 3;
            }
            else
                return 6;
        }
        else if (positionInQuadrat == 1 || positionInQuadrat == 4 || positionInQuadrat == 7)
        {
              if (quadratNumber == 1 || quadratNumber == 4 || quadratNumber == 7)
            {
                return 1;
            }
            else if (quadratNumber == 2 || quadratNumber == 5 || quadratNumber == 8)
            {
                return 4;
            }
            else
                return 7;

        }
        else
        {
            if (quadratNumber == 1 || quadratNumber == 4 || quadratNumber == 7)
            {
                return 2;
            }
            else if (quadratNumber == 2 || quadratNumber == 5 || quadratNumber == 8)
            {
                return 5;
            }
            else
                return 8;
        }

    }


    //workaround für klonen. Sollte später dadurch ersetzt werden, dass die Regeln
    //die interface clonable implementieren
    /**
     * class MyClass implements Cloneable
     * { public MyClass() { } public Object clone()
     * { Cloneable theClone = new MyClass(); // Initialize theClone. return theClone; } }
        Here's some code to create a clone.
        MyClass myObjectClone = (MyClass)myObject.clone();
     *
     */

    public static Rule cloneRule(Rule rule) {

        String ruleName = rule.getName();
        Rule clonedRule = null;
        
        if (ruleName.equals("ColumnOneFree"))
            clonedRule = new ColumnOneFree();
        else if (ruleName.equals("ColumnSeveralFree"))
            clonedRule = new ColumnSeveralFree();
        else if (ruleName.equals("ColumnThreeFree"))
            clonedRule = new ColumnThreeFree();
        else if (ruleName.equals("ColumnTwoFree"))
            clonedRule = new ColumnTwoFree();
        else if (ruleName.equals("QuadratOneFree"))
            clonedRule = new QuadratOneFree();
        else if (ruleName.equals("QuadratTwoFreeCol"))
            clonedRule = new QuadratTwoFreeCol();
        else if (ruleName.equals("QuadratTwoFreeRow"))
            clonedRule = new QuadratTwoFreeRow();
        else if (ruleName.equals("QuadratSeveralFree"))
            clonedRule = new QuadratSeveralFree();
        else if (ruleName.equals("RowOneFree"))
            clonedRule = new RowOneFree();
        else if (ruleName.equals("RowTwoFree"))
            clonedRule = new RowTwoFree();
        else if (ruleName.equals("RowThreeFree"))
            clonedRule = new RowThreeFree();
        else if (ruleName.equals("RowSeveralFree"))
            clonedRule = new RowSeveralFree();
        else if (ruleName.equals("ScanOtherColAndRow"))
            clonedRule = new ScanOtherColAndRow();
        else if (ruleName.equals("RowTwinCandidateElimination"))
            clonedRule = new RowTwinCandidateElimination();
        else if (ruleName.equals("RowInTwoQuadratCompleteOrClosedAndTwoFieldsClosed"))
            clonedRule = new RowInTwoQuadratCompleteOrClosedAndTwoFieldsClosed();
        else
            return rule;

        return clonedRule;

    }

            static public Collection getDigittsSameRowAsCollection(int row, ISudokuMatrix board) throws Exception {
        // gleiche Methode, wie getDigittsSameRow() nur mit Collection als Rückgabewert
        if (row < 0 || row > 8) {
            throw new Exception("Invalid row number");
        }
        Collection digits = new ArrayList();
        for (int i = 0; i < 9; i++) {
            digits.add((Integer) board.getElementAt(row, i));
        }
        return digits;
    }



    static public Collection getDigitsSameColAsCollection (int col, ISudokuMatrix board) throws Exception{
    // gleiche Methode, wie getDigistsSameCol() nur mit Collection als Rückgabewert
        if (col < 0 || col > 8) {
            throw new Exception("Invalid col number");
        }
        Collection digits = new ArrayList();
        for (int i = 0; i < 9; i++) {
            digits.add((Integer) board.getElementAt(i, col));
        }
        return digits;
    }

    public static boolean isFieldEmpty(int row,int col, ISudokuMatrix matrix){
        if((Integer)(matrix.getElementAt(row,col))==0){
            return true;
        }else
            return false;
    }
     public static Collection getRowNeighboursInTheSameQuadrat(int qnr, int col){
      Collection collectionOfNeighbourscols=new ArrayList();
      int colOfFirstField=getColCoordinateFromFirstFieldInQuadrat(qnr);
      if (col != colOfFirstField)
        collectionOfNeighbourscols.add(colOfFirstField);
      if (col != colOfFirstField+1)
          collectionOfNeighbourscols.add(colOfFirstField+1);
      if (col != colOfFirstField+2)
          collectionOfNeighbourscols.add(colOfFirstField+2);
          return collectionOfNeighbourscols;
    }

     public static int getColCoordinateFromFirstFieldInQuadrat(int qnr){
        if((qnr==1) || (qnr==4) || (qnr==7))
            return 0;
       if((qnr==2) || (qnr==5) || (qnr==8))
            return 3;
        if((qnr==3) || (qnr==6) || (qnr==9))
            return 6;
        else
            return -1;
    }
    public static int getRowCoordinateFromFirstFieldInQuadrat(int qnr){
        if((qnr==1) || (qnr==2) || (qnr==3))
            return 0;
       if((qnr==4) || (qnr==5) || (qnr==6))
            return 3;
        if((qnr==7) || (qnr==8) || (qnr==9))
            return 6;
        else
            return -1;
    }
           public static boolean isQuadratClosedForThisDigit(int qnr, int digit,ISudokuMatrix matrix){
       try{
       Collection digits = RuleHelper.getDigitsSameQuadrat(getRowCoordinateFromFirstFieldInQuadrat(qnr), getColCoordinateFromFirstFieldInQuadrat(qnr), matrix);
       Iterator iter = digits.iterator();
       while (iter.hasNext())
       {
        Integer a = (Integer) iter.next();
        if (a == digit)
            return true;       }
    }catch(Exception ex){
        System.out.println(ex.toString());
    }
       return false;
   }
public static boolean isRowInQuadratComplete(int qnr,int row,ISudokuMatrix matrix){
        if (qnr==1 || qnr==4 ||qnr==7){
            if( ((Integer)matrix.getElementAt(row,0) != 0) && ((Integer)matrix.getElementAt(row,1) != 0) && ((Integer)matrix.getElementAt(row,2) != 0))
                return true;
        }
        if (qnr==2 || qnr==5 ||qnr==8)
        {
            if( ((Integer)matrix.getElementAt(row,3) != 0) && ((Integer)matrix.getElementAt(row,4) != 0) && ((Integer)matrix.getElementAt(row,5) != 0))
                return true;
        }
        if (qnr==3 || qnr==6 ||qnr==9)
        {
            if( ((Integer)matrix.getElementAt(row,6) != 0) && ((Integer)matrix.getElementAt(row,7) != 0) && ((Integer)matrix.getElementAt(row,8) != 0))
                return true;
        }
       return false;
    }
    public static Collection getHorizontalQuadratNeighbours(int qnr){
        Collection neighbours=new ArrayList();
        if (qnr==1){
            neighbours.add(2);
            neighbours.add(3);
            return neighbours;
        }
        if (qnr==2){
            neighbours.add(1);
            neighbours.add(3);
            return neighbours;
        }
        if (qnr==3){
            neighbours.add(1);
            neighbours.add(2);
            return neighbours;
        }
        if (qnr==4){
            neighbours.add(5);
            neighbours.add(6);
            return neighbours;
        }
        if (qnr==5){
            neighbours.add(4);
            neighbours.add(6);
            return neighbours;
        }
        if (qnr==6){
            neighbours.add(4);
            neighbours.add(5);
            return neighbours;
        }
        if (qnr==7){
            neighbours.add(8);
            neighbours.add(9);
            return neighbours;
        }
        if (qnr==8){
            neighbours.add(7);
            neighbours.add(9);
            return neighbours;
        }
        if (qnr==9){
            neighbours.add(7);
            neighbours.add(8);
            return neighbours;
        }
        return neighbours;
    }

        public static Collection getVerticalQuadratNeighbours(int qnr){
        Collection neighbours=new ArrayList();
        if (qnr==1){
            neighbours.add(4);
            neighbours.add(7);
            return neighbours;
        }
        if (qnr==2){
            neighbours.add(5);
            neighbours.add(8);
            return neighbours;
        }
        if (qnr==3){
            neighbours.add(6);
            neighbours.add(9);
            return neighbours;
        }
        if (qnr==4){
            neighbours.add(1);
            neighbours.add(7);
            return neighbours;
        }
        if (qnr==5){
            neighbours.add(2);
            neighbours.add(8);
            return neighbours;
        }
        if (qnr==6){
            neighbours.add(3);
            neighbours.add(9);
            return neighbours;
        }
        if (qnr==7){
            neighbours.add(1);
            neighbours.add(4);
            return neighbours;
        }
        if (qnr==8){
            neighbours.add(2);
            neighbours.add(5);
            return neighbours;
        }
        if (qnr==9){
            neighbours.add(3);
            neighbours.add(6);
            return neighbours;
        }
        return neighbours;
    }
    public static Collection getFieldCandidateAfterCheckQuadratRowAndCol(int row, int col, ISudokuMatrix matrix){
        Collection candidat=new ArrayList();
        try{
            candidat = RuleHelper.getMissingdigitsInQuadratOrColOrRow(RuleHelper.getDigittsSameRowAsCollection(row, matrix)); // liefert fehlende Zahlen aus der Zeile
            Collection digitsInQuadrat=getDigitsSameQuadrat(row, col, matrix);
            candidat.removeAll(digitsInQuadrat); // Kandidatenmenge ohne Zahlen im selben Quadrat
            Collection digitsInSameCol=getDigitsSameColAsCollection(col,matrix);
            candidat.removeAll(digitsInSameCol); // Kandidatenmenge ohne Zahlen in selber Spalte
        }catch (Exception ex) {
            Logger.getLogger(RuleHelper.class.getName()).log(Level.SEVERE, null, ex);
        }
        return candidat;
    }
    public static Collection getAllEmptyColNeighbours(int row,int col,ISudokuMatrix matrix ){
        Collection emptyColNeighbours=new ArrayList();
        Collection colNeighbours=getAllColNeighbours(row);
        Iterator iter=colNeighbours.iterator();
        int tempInt;
        while(iter.hasNext()){
            tempInt=(Integer)iter.next();
            if(isFieldEmpty(tempInt,col,matrix))
                emptyColNeighbours.add(tempInt);
        }
        return emptyColNeighbours;
    }
        public static Collection getAllColNeighbours(int col){
        Collection collectionOfNeighboursRows=new ArrayList();
        for(int i=0; i<9; i++){
            collectionOfNeighboursRows.add(i);
        }
        collectionOfNeighboursRows.remove(col);
        return collectionOfNeighboursRows;
    }
    public static boolean containMinOneElement(Collection col_1, Collection col_2){
        Iterator iter=col_2.iterator();
        while(iter.hasNext()){
            if (col_1.contains((Integer)iter.next()))
                return true;
        }
        return false;
    }

   }
